﻿using System;
using System.Net.Sockets;
using System.Net;
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;

namespace Xin478.MySocket
{
    public class TcpServer
    {
        #region 定义字段

        /// <summary> 
        /// 默认的服务器最大连接客户端端数据 
        /// </summary> 
        public const ushort MAXCLIENT = 100;

        /// <summary>
        /// 服务器程序监听的IP地址
        /// </summary>
        private IPAddress _serverIP;

        /// <summary> 
        /// 服务器程序使用的端口 
        /// </summary> 
        private ushort _port;

        /// <summary> 
        /// 服务器程序允许的最大客户端连接数 
        /// </summary> 
        private ushort _maxClient;

        /// <summary> 
        /// 服务器的运行状态 
        /// </summary> 
        private bool _isRun;

        /// <summary> 
        /// 服务器使用的异步Socket类, 
        /// </summary> 
        private Socket _svrSock;

        /// <summary> 
        /// 保存所有客户端会话的哈希表 
        /// </summary> 
        private Hashtable _sessionTable;

        /// <summary> 
        /// 当前的连接的客户端数 
        /// </summary> 
        private ushort _clientCount;

        private IDatagramAnalyze _datagramAnalyze;

        private static object lockHelper = new object();

        #endregion

        #region 事件定义

        /// <summary> 
        /// 客户端建立连接事件 
        /// </summary> 
        public event NetEventHandler OnClientConnectEvent;

        /// <summary> 
        /// 客户端关闭事件 
        /// </summary> 
        public event NetEventHandler OnClientCloseEvent;

        /// <summary> 
        /// 服务器已经满事件 
        /// </summary> 
        public event NetEventHandler OnServerFullEvent;

        /// <summary> 
        /// 服务器接收到数据事件 
        /// </summary> 
        public event NetEventHandler OnReceivedDatagramEvent;
        #endregion

        #region 构造函数
        public TcpServer(string ipAddress, ushort port)
        {
            this._serverIP = IPAddress.Parse(ipAddress);
            this._port = port;
            this._maxClient = MAXCLIENT;
        }
        public TcpServer(string ipAddress, ushort port, ushort maxClient)
        {
            this._serverIP = IPAddress.Parse(ipAddress);
            this._port = port;
            this._maxClient = maxClient;
        }
        public TcpServer(IPAddress serverIp, ushort port)
        {
            this._serverIP = serverIp;
            this._port = port;
            this._maxClient = MAXCLIENT;
        }
        public TcpServer(IPAddress serverIp, ushort port, ushort maxClient)
        {
            this._serverIP = serverIp;
            this._port = port;
            this._maxClient = maxClient;
        }
        #endregion

        #region 属性
        public IDatagramAnalyze DatagramAnalyze
        {
            set { this._datagramAnalyze = value as IDatagramAnalyze; }
            get { return this._datagramAnalyze as IDatagramAnalyze; }
        }

        /// <summary> 
        /// 服务器的Socket对象 
        /// </summary> 
        public Socket ServerSocket
        {
            get
            {
                return this._svrSock;
            }
        }

        /// <summary> 
        /// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改 
        /// </summary> 
        public Hashtable SessionTable
        {
            get
            {
                return this._sessionTable;
            }
        }

        /// <summary> 
        /// 服务器可以容纳客户端的最大能力 
        /// </summary> 
        public int Capacity
        {
            get
            {
                return this._maxClient;
            }
        }

        /// <summary> 
        /// 当前的客户端连接数 
        /// </summary> 
        public int ClientCount
        {
            get
            {
                return this._clientCount;
            }
        }

        /// <summary> 
        /// 服务器运行状态 
        /// </summary> 
        public bool IsRun
        {
            get
            {
                return _isRun;
            }

        }
        #endregion

        #region 公有方法

        /// <summary> 
        /// 启动服务器程序,开始监听客户端请求 
        /// </summary> 
        public void Start()
        {
            if (this.IsRun) throw new ApplicationException("该实例已在运行中");

            this._sessionTable = new Hashtable(53);

            this._svrSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint iep = new IPEndPoint(this._serverIP, this._port);
            try
            {
                this._svrSock.Bind(iep);
            }
            catch (SocketException se)
            {
                throw se;
            }
            catch
            {
                return;
            }

            this._svrSock.Listen(5);
            this._svrSock.BeginAccept(new AsyncCallback(AcceptConn), this._svrSock);
            this._isRun = true;
        }

        /// <summary> 
        /// 停止服务器程序,所有与客户端的连接将关闭 
        /// </summary> 
        public void Stop()
        {
            if (!this._isRun) return;

            if (_svrSock.Connected)
            {
                _svrSock.Shutdown(SocketShutdown.Both);
            }

            this.CloseAllClient();
            _svrSock.Close();
            _sessionTable = null;
            this._isRun = false;
        }

        public virtual void CloseAllClient()
        {
            if (!this._isRun) return;

            foreach (Session client in _sessionTable.Values)
            {
                client.Close();
            }

            _sessionTable.Clear();
        }

        /// <summary> 
        /// 关闭一个与客户端之间的会话 
        /// </summary> 
        /// <param name="closeClient">需要关闭的客户端会话对象</param> 
        public virtual void CloseSession(Session closeClient)
        {
            Debug.Assert(closeClient != null);

            if (closeClient != null)
            {

                closeClient.Datagram = null;

                _sessionTable.Remove(closeClient.ID);

                _clientCount--;

                //客户端强制关闭链接 
                if (this.OnClientCloseEvent != null)
                {
                    OnClientCloseEvent(this, new NetEventArgs(closeClient));
                }

                closeClient.Close();
            }
        }

        public virtual void Send(Session recvDataClient, byte[] datagram)
        {
            if (datagram == null) return;
            datagram = this._datagramAnalyze.Assemble(datagram);
            
            recvDataClient.ClientSocket.BeginSend(datagram, 0, datagram.Length, SocketFlags.None,
                    new AsyncCallback(SendDataEnd), recvDataClient.ClientSocket);
        }

        #endregion

        #region 受保护方法

        /// <summary> 
        /// 关闭一个客户端Socket,首先需要关闭Session 
        /// </summary> 
        /// <param name="client">目标Socket对象</param> 
        /// <param name="exitType">客户端退出的类型</param> 
        protected virtual void CloseClient(Socket client, Session.ExitType exitType)
        {
            Debug.Assert(client != null);

            //查找该客户端是否存在,如果不存在,抛出异常 
            Session closeClient = FindSession(client);

            closeClient.TypeOfExit = exitType;

            if (closeClient != null)
            {
                CloseSession(closeClient);
            }
            else
            {
                throw (new ApplicationException("需要关闭的Socket对象不存在"));
            }
        }

        /// <summary> 
        /// 客户端连接处理函数 
        /// </summary> 
        /// <param name="iar">欲建立服务器连接的Socket对象</param> 
        protected virtual void AcceptConn(IAsyncResult iar)
        {
            //如果服务器停止了服务,就不能再接收新的客户端 
            if (!_isRun)
            {
                return;
            }

            //接受一个客户端的连接请求 
            Socket oldserver = (Socket)iar.AsyncState;
            Socket client = oldserver.EndAccept(iar);

            //检查是否达到最大的允许的客户端数目 
            if (_clientCount == _maxClient)
            {
                //服务器已满,发出通知 
                if (this.OnServerFullEvent != null)
                {
                    OnServerFullEvent(this, new NetEventArgs(new Session(client)));
                }

            }
            else
            {

                Session newSession = new Session(client);
                _sessionTable.Add(newSession.ID, newSession);               
                _clientCount++; //客户端引用计数+1 
                StateObject state = new StateObject();
                state.workSocket = client;

                //开始接受来自该客户端的数据 
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveData), state);

                //新的客户段连接,发出通知 
                if (this.OnClientConnectEvent != null)
                {
                    OnClientConnectEvent(this, new NetEventArgs(newSession));
                }
            }

            //继续接受客户端 
            _svrSock.BeginAccept(new AsyncCallback(AcceptConn), _svrSock);
        }

        /// <summary> 
        /// 通过Socket对象查找Session对象 
        /// </summary> 
        /// <param name="client"></param> 
        /// <returns>找到的Session对象,如果为null,说明并不存在该回话</returns> 
        private Session FindSession(Socket client)
        {
            SessionId id = new SessionId((int)client.Handle);

            return (Session)_sessionTable[id];
        }

        /// <summary> 
        /// 接受数据完成处理函数，异步的特性就体现在这个函数中， 
        /// 收到数据后，会自动解析为字符串报文 
        /// </summary> 
        /// <param name="iar">目标客户端Socket</param> 
        protected virtual void ReceiveData(IAsyncResult iar)
        {
            if(this._datagramAnalyze==null) throw new ArgumentNullException("datagramAnalyze");

            StateObject state = (StateObject)iar.AsyncState;
            Socket client = state.workSocket;
            try
            {
                //如果两次开始了异步的接收,所以当客户端退出的时候会两次执行EndReceive 
                int recv = client.EndReceive(iar);
                if (recv == 0)
                {
                    //正常的关闭 
                    this.CloseClient(client, Session.ExitType.NormalExit);
                    return;
                }

                //发布收到数据的事件 
                Session sendDataSession = FindSession(client);
                IList<byte[]> recvDatagrams = this.DatagramAnalyze.Disassemble(sendDataSession, state.buffer, recv);
                
                if (this.OnReceivedDatagramEvent != null)
                {    
                    foreach (byte[] newDatagram in recvDatagrams)
                    {
                        ICloneable copySession = (ICloneable)sendDataSession;//Need Deep Copy.因为需要保证多个不同报文独立存在 
                        Session clientSession = (Session)copySession.Clone();
                        clientSession.Datagram = newDatagram;

                        this.OnReceivedDatagramEvent(this, new NetEventArgs(clientSession)); //发布一个报文消息 
                         
                    }                    
                }

                //继续接收来自来客户端的数据 
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveData), state);

            }
            catch (SocketException ex)
            {
                //客户端退出 
                if (10054 == ex.ErrorCode)
                {
                    //客户端强制关闭 
                    CloseClient(client, Session.ExitType.ExceptionExit);
                }

            }
            catch (ObjectDisposedException ex)
            {
                //这里的实现不够优雅 
                //当调用CloseSession()时,会结束数据接收,但是数据接收 
                //处理中会调用int recv = client.EndReceive(iar); 
                //就访问了CloseSession()已经处置的对象 
                //我想这样的实现方法也是无伤大雅的. 
                if (ex != null)
                {
                    ex = null;
                    //DoNothing; 
                }
            }

        }

        /// <summary> 
        /// 发送数据完成处理函数 
        /// </summary> 
        /// <param name="iar">目标客户端Socket</param> 
        protected virtual void SendDataEnd(IAsyncResult iar)
        {
            Socket client = (Socket)iar.AsyncState;

            int sent = client.EndSend(iar);
        }

        #endregion
    }
}
